package com.yycx.base.provider.configuration;

import com.yycx.base.provider.handler.LoginSuccesssHandler;
import com.yycx.base.provider.handler.LogoutSuccessHandler;
import com.yycx.common.base.constants.FlymeConstants;
import com.yycx.common.configuration.OpenCommonProperties;
import com.yycx.common.exception.OpenAccessDeniedHandler;
import com.yycx.common.exception.OpenAuthenticationEntryPoint;
import com.yycx.common.security.TokenUtils;
import com.yycx.common.security.enums.TokenStoreEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * oauth2资源服务器配置
 * @author: zyf
 * @date: 2018/10/23 10:31
 * @description:
 */
@Slf4j
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    @Resource
    private RedisConnectionFactory redisConnectionFactory;

    @Resource
    private DataSource dataSource;

    @Resource
    private PasswordEncoder passwordEncoder;


    @Resource
    private PermitAllSecurityConfig permitAllSecurityConfig;

    @Resource
    private LoginSuccesssHandler loginSuccesssHandler;

    @Resource
    private OpenCommonProperties openCommonProperties;

    @Autowired
    private LogoutSuccessHandler logoutSuccessHandler;


    @Bean
    public RedisTokenStore redisTokenStore() {
        return new RedisTokenStore(redisConnectionFactory);
    }



    private OAuth2WebSecurityExpressionHandler expressionHandler;


    @Bean
    public JdbcClientDetailsService clientDetailsService() {
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
        jdbcClientDetailsService.setPasswordEncoder(passwordEncoder);
        return jdbcClientDetailsService;
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        String tokenStoreType = FlymeConstants.BASE_TOKEN_STORE;
        if (tokenStoreType.equals(TokenStoreEnum.JDBC.name())) {
            resources.tokenServices(TokenUtils.buildJdbcTokenServices(dataSource));
        }
        if (tokenStoreType.equals(TokenStoreEnum.REDIS.name())) {
            resources.tokenServices(TokenUtils.buildRedisTokenServices(redisConnectionFactory));
        }
        if (tokenStoreType.equals(TokenStoreEnum.JWT.name())) {
            resources.tokenServices(TokenUtils.buildJwtTokenServices(openCommonProperties));
        }
        resources.authenticationEntryPoint(new OpenAuthenticationEntryPoint()).accessDeniedHandler(new OpenAccessDeniedHandler());
        resources.expressionHandler(expressionHandler);
    }

    @Bean
    public OAuth2WebSecurityExpressionHandler oAuth2WebSecurityExpressionHandler(ApplicationContext applicationContext) {
        expressionHandler = new OAuth2WebSecurityExpressionHandler();
        expressionHandler.setApplicationContext(applicationContext);
        return expressionHandler;
    }


    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.headers().frameOptions().disable();
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                //只有超级管理员角色可执行远程端点
                // .requestMatchers(EndpointRequest.toAnyEndpoint()).hasAnyAuthority(CommonConstants.AUTHORITY_ACTUATOR)
                // 内部feign调用直接放行
                .antMatchers(
                        "/login/**",
                        "/logout/**",
                        "/oauth/**",
                        "/**/common/**",
                        "/**/websocket/**",
                        "/**/jmreport/**",
                        "/**/generate/**",
                        "/**/report/**",
                        "/**/ureport/**",
                        "/account/logs/add",
                        "/authority/access",
                        "/authority/app",
                        "/account/reset/password",
                        "/app/*/info",
                        "/base/**",
                        "/dict/initDict",
                        "/api/*/info",
                        "/app/client/*/info",
                        "/api/client/*/info",
                        "/user/info",
                        "/gateway/api/**",
                        "/api/infoByServiceIdAndPath"
                ).permitAll()
                .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
                .anyRequest().access("@baseAuthorizationManager.check(request,authentication)")
                .and()
                .formLogin().loginPage("/login").permitAll()
                //.successHandler(loginSuccesssHandler)
                .and()
                .apply(permitAllSecurityConfig)
                .and()
                .logout().permitAll()
                // /logout退出清除cookie
                .addLogoutHandler(new CookieClearingLogoutHandler("token", "remember-me"))
                .logoutSuccessHandler(logoutSuccessHandler)
                .and()
                //认证鉴权错误处理,为了统一异常处理。每个资源服务器都应该加上。
                .exceptionHandling()
                .accessDeniedHandler(new OpenAccessDeniedHandler())
                .authenticationEntryPoint(new OpenAuthenticationEntryPoint())
                .and()
                .csrf().disable()
                // 禁用httpBasic
                .httpBasic().disable();
    }

}

